<!DOCTYPE html>
<!-- saved from url=(0118)http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3 -->
<html lang="en" dir="ltr" class="client-js"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Complete Roguelike Tutorial, using python+libtcod, part 3 - RogueBasin</title>
<meta charset="UTF-8">
<meta name="generator" content="MediaWiki 1.19.2">
<link rel="shortcut icon" href="http://roguebasin.roguelikedevelopment.org/favicon.ico">
<link rel="search" type="application/opensearchdescription+xml" href="http://roguebasin.roguelikedevelopment.org/opensearch_desc.php" title="RogueBasin (en)">
<link rel="EditURI" type="application/rsd+xml" href="http://roguebasin.roguelikedevelopment.org/api.php?action=rsd">
<link rel="alternate" type="application/atom+xml" title="RogueBasin Atom feed" href="http://roguebasin.roguelikedevelopment.org/index.php?title=Special:RecentChanges&feed=atom">
<link rel="stylesheet" href="http://roguebasin.roguelikedevelopment.org/load.php?debug=false&lang=en&modules=mediawiki.legacy.commonPrint%2Cshared%7Cskins.monobook&only=styles&skin=monobook&*">
<!--[if IE 6]><link rel="stylesheet" href="/skins/monobook/IE60Fixes.css?303" media="screen" /><![endif]-->
<!--[if IE 7]><link rel="stylesheet" href="/skins/monobook/IE70Fixes.css?303" media="screen" /><![endif]--><style type="text/css" media="all">.js-messagebox{margin:1em 5%;padding:0.5em 2.5%;border:1px solid #ccc;background-color:#fcfcfc;font-size:0.8em}.js-messagebox .js-messagebox-group{margin:1px;padding:0.5em 2.5%;border-bottom:1px solid #ddd}.js-messagebox .js-messagebox-group:last-child{border-bottom:thin none transparent}

/* cache key: 133099-basin:resourceloader:filter:minify-css:7:8b08bdc91c52a9ffba396dccfb5b473c */


.mw-collapsible-toggle{float:right} li .mw-collapsible-toggle{float:none} .mw-collapsible-toggle-li{list-style:none}

/* cache key: 133099-basin:resourceloader:filter:minify-css:7:4250852ed2349a0d4d0fc6509a3e7d4c */
</style><meta name="ResourceLoaderDynamicStyles" content="">
<link rel="stylesheet" href="http://roguebasin.roguelikedevelopment.org/load.php?debug=false&lang=en&modules=site&only=styles&skin=monobook&*">
<style>a:lang(ar),a:lang(ckb),a:lang(fa),a:lang(kk-arab),a:lang(mzn),a:lang(ps),a:lang(ur){text-decoration:none}a.new,#quickbar a.new{color:#ba0000}

/* cache key: 133099-basin:resourceloader:filter:minify-css:7:c88e2bcd56513749bec09a7e29cb3ffa */
</style>

<script src="./Complete Roguelike Tutorial, using python+libtcod, part 3 - RogueBasin_files/load.php"></script><style type="text/css"></style><script src="./Complete Roguelike Tutorial, using python+libtcod, part 3 - RogueBasin_files/load(1).php"></script>
<script>if(window.mw){
mw.config.set({"wgCanonicalNamespace":"","wgCanonicalSpecialPageName":false,"wgNamespaceNumber":0,"wgPageName":"Complete_Roguelike_Tutorial,_using_python+libtcod,_part_3","wgTitle":"Complete Roguelike Tutorial, using python+libtcod, part 3","wgCurRevisionId":30347,"wgArticleId":3607,"wgIsArticle":true,"wgAction":"view","wgUserName":null,"wgUserGroups":["*"],"wgCategories":["Developing"],"wgBreakFrames":false,"wgPageContentLanguage":"en","wgSeparatorTransformTable":["",""],"wgDigitTransformTable":["",""],"wgRelevantPageName":"Complete_Roguelike_Tutorial,_using_python+libtcod,_part_3","wgRestrictionEdit":[],"wgRestrictionMove":[]});
}</script><script>if(window.mw){
mw.loader.implement("user.options",function($){mw.user.options.set({"ccmeonemails":0,"cols":80,"date":"default","diffonly":0,"disablemail":0,"disablesuggest":0,"editfont":"default","editondblclick":0,"editsection":1,"editsectiononrightclick":0,"enotifminoredits":0,"enotifrevealaddr":0,"enotifusertalkpages":1,"enotifwatchlistpages":0,"extendwatchlist":0,"externaldiff":0,"externaleditor":0,"fancysig":0,"forceeditsummary":0,"gender":"unknown","hideminor":0,"hidepatrolled":0,"highlightbroken":1,"imagesize":2,"justify":0,"math":1,"minordefault":0,"newpageshidepatrolled":0,"nocache":0,"noconvertlink":0,"norollbackdiff":0,"numberheadings":0,"previewonfirst":0,"previewontop":1,"quickbar":5,"rcdays":7,"rclimit":50,"rememberpassword":0,"rows":25,"searchlimit":20,"showhiddencats":0,"showjumplinks":1,"shownumberswatching":1,"showtoc":1,"showtoolbar":1,"skin":"monobook","stubthreshold":0,"thumbsize":2,"underline":2,"uselivepreview":0,"usenewrc":0,"watchcreations":0,"watchdefault":0,"watchdeletion":
0,"watchlistdays":3,"watchlisthideanons":0,"watchlisthidebots":0,"watchlisthideliu":0,"watchlisthideminor":0,"watchlisthideown":0,"watchlisthidepatrolled":0,"watchmoves":0,"wllimit":250,"variant":"en","language":"en","searchNs0":true,"searchNs1":false,"searchNs2":false,"searchNs3":false,"searchNs4":false,"searchNs5":false,"searchNs6":false,"searchNs7":false,"searchNs8":false,"searchNs9":false,"searchNs10":false,"searchNs11":false,"searchNs12":false,"searchNs13":false,"searchNs14":false,"searchNs15":false});;},{},{});mw.loader.implement("user.tokens",function($){mw.user.tokens.set({"editToken":"+\\","watchToken":false});;},{},{});

/* cache key: 133099-basin:resourceloader:filter:minify-js:7:74a832f2292f1f4d40d425d223444e78 */
}</script>
<script>if(window.mw){
mw.loader.load(["mediawiki.page.startup","mediawiki.legacy.wikibits","mediawiki.legacy.ajax"]);
}</script><script type="text/javascript" src="./Complete Roguelike Tutorial, using python+libtcod, part 3 - RogueBasin_files/load(2).php"></script>
<style type="text/css">/*<![CDATA[*/
.source-python {line-height: normal;}
.source-python li, .source-python pre {
	line-height: normal; border: 0px none white;
}
/**
 * GeSHi Dynamically Generated Stylesheet
 * --------------------------------------
 * Dynamically generated stylesheet for python
 * CSS class: source-python, CSS id: 
 * GeSHi (C) 2004 - 2007 Nigel McNie, 2007 - 2008 Benny Baumann
 * (http://qbnz.com/highlighter/ and http://geshi.org/)
 * --------------------------------------
 */
.python.source-python .de1, .python.source-python .de2 {font: normal normal 1em/1.2em monospace; margin:0; padding:0; background:none; vertical-align:top;}
.python.source-python  {font-family:monospace;}
.python.source-python .imp {font-weight: bold; color: red;}
.python.source-python li, .python.source-python .li1 {font-weight: normal; vertical-align:top;}
.python.source-python .ln {width:1px;text-align:right;margin:0;padding:0 2px;vertical-align:top;}
.python.source-python .li2 {font-weight: bold; vertical-align:top;}
.python.source-python .kw1 {color: #ff7700;font-weight:bold;}
.python.source-python .kw2 {color: #008000;}
.python.source-python .kw3 {color: #dc143c;}
.python.source-python .kw4 {color: #0000cd;}
.python.source-python .co1 {color: #808080; font-style: italic;}
.python.source-python .coMULTI {color: #808080; font-style: italic;}
.python.source-python .es0 {color: #000099; font-weight: bold;}
.python.source-python .br0 {color: black;}
.python.source-python .sy0 {color: #66cc66;}
.python.source-python .st0 {color: #483d8b;}
.python.source-python .nu0 {color: #ff4500;}
.python.source-python .me1 {color: black;}
.python.source-python .ln-xtra, .python.source-python li.ln-xtra, .python.source-python div.ln-xtra {background-color: #ffc;}
.python.source-python span.xtra { display:block; }

/*]]>*/
</style>
<style type="text/css">/*<![CDATA[*/
@import "/index.php?title=MediaWiki:Geshi.css&usemsgcache=yes&action=raw&ctype=text/css&smaxage=18000";
/*]]>*/
</style></head>
<body class="mediawiki ltr sitedir-ltr ns-0 ns-subject page-Complete_Roguelike_Tutorial_using_python_libtcod_part_3 skin-monobook action-view">
<div id="globalWrapper">
<div id="column-content"><div id="content"><div id="mw-js-message" class="js-messagebox" style="display: none;"></div>
	<a id="top"></a>
	<div id="siteNotice"><div id="localNotice" lang="en" dir="ltr"></div></div>
	<h1 id="firstHeading" class="firstHeading"><span dir="auto">Complete Roguelike Tutorial, using python+libtcod, part 3</span></h1>
	<div id="bodyContent" class="mw-body">
		<div id="siteSub">From RogueBasin</div>
		<div id="contentSub"></div>
		<div id="jump-to-nav" class="mw-jump">Jump to: <a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3#column-one">navigation</a>, <a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3#searchInput">search</a></div>
		<!-- start content -->
<div id="mw-content-text" lang="en" dir="ltr" class="mw-content-ltr"><center><table border="0" cellpadding="10" cellspacing="0" style="background:#F0E68C"><tbody><tr><td><center>
<p>This is part of a series of tutorials; the main page can be found <a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod" title="Complete Roguelike Tutorial, using python+libtcod">here</a>.
</p><p>The tutorial uses libctod version 1.5.1. If you prefer to use 1.5.0, you can find the old version <a rel="nofollow" class="external text" href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3&oldid=29865">here</a>
</p>
</center></td></tr></tbody></table></center>
<table id="toc" class="toc"><tbody><tr><td><div id="toctitle"><h2>Contents</h2><span class="toctoggle">&nbsp;[<a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3#" class="internal" id="togglelink">hide</a>]&nbsp;</span></div>
<ul>
<li class="toclevel-1"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3#The_Dungeon"><span class="tocnumber">1</span> <span class="toctext"><b>The Dungeon</b></span></a>
<ul>
<li class="toclevel-2 tocsection-1"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3#Dungeon_building_blocks"><span class="tocnumber">1.1</span> <span class="toctext">Dungeon building blocks</span></a></li>
<li class="toclevel-2 tocsection-2"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3#Dungeon_generator"><span class="tocnumber">1.2</span> <span class="toctext">Dungeon generator</span></a></li>
<li class="toclevel-2 tocsection-3"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3#Optional_part:_Visualizing_the_dungeon"><span class="tocnumber">1.3</span> <span class="toctext">Optional part: Visualizing the dungeon</span></a></li>
</ul>
</li>
</ul>
</td></tr></tbody></table>
<p><br>
</p>
<center><h1> <span class="mw-headline" id="The_Dungeon"><b>The Dungeon</b></span></h1></center>
<h2> <span class="mw-headline" id="Dungeon_building_blocks"> Dungeon building blocks </span></h2>
<p>Alright then, it's about time our dungeon takes a recognizable shape! I never cared much for pillars anyway. What we're gonna do now is create functions to carve rooms and tunnels in the underground rock. These functions will be the building blocks used to generate the whole dungeon which you'll get to later. 
</p><p>First of all, a little helper class that will be very handy when dealing with rectangular rooms:
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1"><span class="kw1">class</span> Rect:
    <span class="co1">#a rectangle on the map. used to characterize a room.</span>
    <span class="kw1">def</span> <span class="kw4">__init__</span><span class="br0">(</span><span class="kw2">self</span>, x, y, w, h<span class="br0">)</span>:
        <span class="kw2">self</span>.<span class="me1">x1</span> = x
        <span class="kw2">self</span>.<span class="me1">y1</span> = y
        <span class="kw2">self</span>.<span class="me1">x2</span> = x + w
        <span class="kw2">self</span>.<span class="me1">y2</span> = y + h</pre></div></div></div>
<p><br>
This will take top-left coordinates for a rectangle (in tiles, of course), and its size, to define it in terms of two points: top-left (x1, y1) and bottom-right (x2, y2). Why not store directly its top-left coordinates and size? Well, it's much easier to loop through the room's tiles this way, since Python's <i>range</i> function takes arguments in this form. It will also be apparent later that the code for intersecting rectangles (for example, to be sure rooms don't overlap) is easier to define this way.
</p><p>Python's <i>range</i> function, however, excludes the last element in the loop. For example, <i>for x in range(x1, x2)</i> will only loop until <i>x2 - 1</i>. So the code to fill a room with unblocked tiles could be:
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1"><span class="kw1">def</span> create_room<span class="br0">(</span>room<span class="br0">)</span>:
    <span class="kw1">global</span> <span class="kw2">map</span>
    <span class="co1">#go through the tiles in the rectangle and make them passable</span>
    <span class="kw1">for</span> x <span class="kw1">in</span> <span class="kw2">range</span><span class="br0">(</span>room.<span class="me1">x1</span>, room.<span class="me1">x2</span> + <span class="nu0">1</span><span class="br0">)</span>:
        <span class="kw1">for</span> y <span class="kw1">in</span> <span class="kw2">range</span><span class="br0">(</span>room.<span class="me1">y1</span>, room.<span class="me1">y2</span> + <span class="nu0">1</span><span class="br0">)</span>:
            <span class="kw2">map</span><span class="br0">[</span>x<span class="br0">]</span><span class="br0">[</span>y<span class="br0">]</span>.<span class="me1">blocked</span> = <span class="kw2">False</span>
            <span class="kw2">map</span><span class="br0">[</span>x<span class="br0">]</span><span class="br0">[</span>y<span class="br0">]</span>.<span class="me1">block_sight</span> = <span class="kw2">False</span></pre></div></div></div>
<p><br>
The <i>+ 1</i> at the end takes care of the issue we mentioned. But we actually want to leave some walls at the border of the room, so we'll leave out one tile in all directions.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1"><span class="kw1">def</span> create_room<span class="br0">(</span>room<span class="br0">)</span>:
    <span class="kw1">global</span> <span class="kw2">map</span>
    <span class="co1">#go through the tiles in the rectangle and make them passable</span>
    <span class="kw1">for</span> x <span class="kw1">in</span> <span class="kw2">range</span><span class="br0">(</span>room.<span class="me1">x1</span> + <span class="nu0">1</span>, room.<span class="me1">x2</span><span class="br0">)</span>:
        <span class="kw1">for</span> y <span class="kw1">in</span> <span class="kw2">range</span><span class="br0">(</span>room.<span class="me1">y1</span> + <span class="nu0">1</span>, room.<span class="me1">y2</span><span class="br0">)</span>:
            <span class="kw2">map</span><span class="br0">[</span>x<span class="br0">]</span><span class="br0">[</span>y<span class="br0">]</span>.<span class="me1">blocked</span> = <span class="kw2">False</span>
            <span class="kw2">map</span><span class="br0">[</span>x<span class="br0">]</span><span class="br0">[</span>y<span class="br0">]</span>.<span class="me1">block_sight</span> = <span class="kw2">False</span></pre></div></div></div>
<p><br>
Subtle, but important! This way, even if two rooms are right next to each other (but not overlapping), there's always one wall separating them. Now we're going to modify the <i>make_map</i> function to start out with all tiles blocked, and carve two rooms in the map with our new function.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1"><span class="kw1">def</span> make_map<span class="br0">(</span><span class="br0">)</span>:
    <span class="kw1">global</span> <span class="kw2">map</span>
&nbsp;
    <span class="co1">#fill map with "blocked" tiles</span>
    <span class="kw2">map</span> = <span class="br0">[</span><span class="br0">[</span> Tile<span class="br0">(</span><span class="kw2">True</span><span class="br0">)</span>
        <span class="kw1">for</span> y <span class="kw1">in</span> <span class="kw2">range</span><span class="br0">(</span>MAP_HEIGHT<span class="br0">)</span> <span class="br0">]</span>
            <span class="kw1">for</span> x <span class="kw1">in</span> <span class="kw2">range</span><span class="br0">(</span>MAP_WIDTH<span class="br0">)</span> <span class="br0">]</span>
&nbsp;
    <span class="co1">#create two rooms</span>
    room1 = Rect<span class="br0">(</span><span class="nu0">20</span>, <span class="nu0">15</span>, <span class="nu0">10</span>, <span class="nu0">15</span><span class="br0">)</span>
    room2 = Rect<span class="br0">(</span><span class="nu0">50</span>, <span class="nu0">15</span>, <span class="nu0">10</span>, <span class="nu0">15</span><span class="br0">)</span>
    create_room<span class="br0">(</span>room1<span class="br0">)</span>
    create_room<span class="br0">(</span>room2<span class="br0">)</span></pre></div></div></div>
<p><br>
Before testing out, make the player appear in the center of the first room:
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">    player.<span class="me1">x</span> = <span class="nu0">25</span>
    player.<span class="me1">y</span> = <span class="nu0">23</span></pre></div></div></div>
<p><br>
You can walk around the first room, but not reach the second. We'll define a function to carve a horizontal tunnel:
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1"><span class="kw1">def</span> create_h_tunnel<span class="br0">(</span>x1, x2, y<span class="br0">)</span>:
    <span class="kw1">global</span> <span class="kw2">map</span>
    <span class="kw1">for</span> x <span class="kw1">in</span> <span class="kw2">range</span><span class="br0">(</span><span class="kw2">min</span><span class="br0">(</span>x1, x2<span class="br0">)</span>, <span class="kw2">max</span><span class="br0">(</span>x1, x2<span class="br0">)</span> + <span class="nu0">1</span><span class="br0">)</span>:
        <span class="kw2">map</span><span class="br0">[</span>x<span class="br0">]</span><span class="br0">[</span>y<span class="br0">]</span>.<span class="me1">blocked</span> = <span class="kw2">False</span>
        <span class="kw2">map</span><span class="br0">[</span>x<span class="br0">]</span><span class="br0">[</span>y<span class="br0">]</span>.<span class="me1">block_sight</span> = <span class="kw2">False</span></pre></div></div></div>
<p><br>
There's some creative use of the <i>min</i> and <i>max</i> functions there, so we'll explain that a bit. Feel free to skip this if you got it. They'll return the minimum or maximum of both arguments, but why are they needed? Well, if x1 &lt; x2, x1 will be the minimum of both, and x2 the maximum. So that line will be the same as:
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">    <span class="kw1">for</span> x <span class="kw1">in</span> <span class="kw2">range</span><span class="br0">(</span>x1, x2 + <span class="nu0">1</span><span class="br0">)</span>:</pre></div></div></div>
<p><br>
If they're reversed, the same line wouldn't work -- as it is, <i>for</i> only loops from a small number to a bigger number. But returning to the original line, then x2 will be the minimum, and x1 maximum. You guessed it -- it will be the same as:
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">    <span class="kw1">for</span> x <span class="kw1">in</span> <span class="kw2">range</span><span class="br0">(</span>x2, x1 + <span class="nu0">1</span><span class="br0">)</span>:</pre></div></div></div>
<p><br>
It could be solved with other logic: swapping their values, changing the <i>for</i> 's step to negative, having an <i>if</i> to choose between the two lines... The functions <i>min</i> and <i>max</i> tend to give the shortest code, though it may be harder to understand if you're not used to them much.
</p><p>The function to carve vertical functions is pretty similar.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1"><span class="kw1">def</span> create_v_tunnel<span class="br0">(</span>y1, y2, x<span class="br0">)</span>:
    <span class="kw1">global</span> <span class="kw2">map</span>
    <span class="co1">#vertical tunnel</span>
    <span class="kw1">for</span> y <span class="kw1">in</span> <span class="kw2">range</span><span class="br0">(</span><span class="kw2">min</span><span class="br0">(</span>y1, y2<span class="br0">)</span>, <span class="kw2">max</span><span class="br0">(</span>y1, y2<span class="br0">)</span> + <span class="nu0">1</span><span class="br0">)</span>:
        <span class="kw2">map</span><span class="br0">[</span>x<span class="br0">]</span><span class="br0">[</span>y<span class="br0">]</span>.<span class="me1">blocked</span> = <span class="kw2">False</span>
        <span class="kw2">map</span><span class="br0">[</span>x<span class="br0">]</span><span class="br0">[</span>y<span class="br0">]</span>.<span class="me1">block_sight</span> = <span class="kw2">False</span></pre></div></div></div>
<p><br>
Now, in <i>make_map</i>, we can connect both rooms with a horizontal tunnel.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">create_h_tunnel<span class="br0">(</span><span class="nu0">25</span>, <span class="nu0">55</span>, <span class="nu0">23</span><span class="br0">)</span></pre></div></div></div>
<p><br>
That concludes the test for the carving functions! It's really starting to look better by the minute. You can create different dungeon configurations with them, but it's tricky defining the coordinates by hand and we're going to rush right away to the most interesting bit: random dungeon generation!
</p><p>You can find the whole code for this part <a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3_code#Dungeon_building_blocks" title="Complete Roguelike Tutorial, using python+libtcod, part 3 code">here</a>.
</p><p><br>
</p>
<h2> <span class="mw-headline" id="Dungeon_generator"> Dungeon generator </span></h2>
<p>Finally, the dungeon generator. After this point you'll have accomplished something that is truly a turning point in the development of a roguelike, and that is no small feat! There are many different approaches to coding a dungeon generator, and there is no consensus; everyone has their own interpretation of the right way to do this. Perhaps after being content with this one for a while you'll think of different ways to improve it.
</p><p>The basic idea is this. Pick a random location for the first room, and carve it. Then pick another location for the second; it cannot overlap with the first. Connect the two with a tunnel, and repeat. This will yield a sequence of connected rooms.
</p><p>One important property is that the player is guaranteed to be able to reach every room. The downside is that there will be lots of overlaps between the tunnels, and even tunnels overlapping rooms, but that's not too bad since the player can't distinguish between tunnel floor and room floor. The overlaps actually give it a more complicated look than if it were just a simple string of rooms!
</p><p>Alright, we can see that the <i>Rect</i> class needs a method to check for intersections, so that no two rooms can overlap. We'll also create a method to return the center coordinates of the room since that's where all tunnels will be connected.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">    <span class="kw1">def</span> center<span class="br0">(</span><span class="kw2">self</span><span class="br0">)</span>:
        center_x = <span class="br0">(</span><span class="kw2">self</span>.<span class="me1">x1</span> + <span class="kw2">self</span>.<span class="me1">x2</span><span class="br0">)</span> / <span class="nu0">2</span>
        center_y = <span class="br0">(</span><span class="kw2">self</span>.<span class="me1">y1</span> + <span class="kw2">self</span>.<span class="me1">y2</span><span class="br0">)</span> / <span class="nu0">2</span>
        <span class="kw1">return</span> <span class="br0">(</span>center_x, center_y<span class="br0">)</span>
&nbsp;
    <span class="kw1">def</span> intersect<span class="br0">(</span><span class="kw2">self</span>, other<span class="br0">)</span>:
        <span class="co1">#returns true if this rectangle intersects with another one</span>
        <span class="kw1">return</span> <span class="br0">(</span><span class="kw2">self</span>.<span class="me1">x1</span> <span class="sy0">&lt;</span>= other.<span class="me1">x2</span> <span class="kw1">and</span> <span class="kw2">self</span>.<span class="me1">x2</span> <span class="sy0">&gt;</span>= other.<span class="me1">x1</span> <span class="kw1">and</span>
                <span class="kw2">self</span>.<span class="me1">y1</span> <span class="sy0">&lt;</span>= other.<span class="me1">y2</span> <span class="kw1">and</span> <span class="kw2">self</span>.<span class="me1">y2</span> <span class="sy0">&gt;</span>= other.<span class="me1">y1</span><span class="br0">)</span></pre></div></div></div>
<p><br>
Don't worry if the intersection logic seems alien, it's a direct formula that doesn't require interpretation! It's one of those copy-and-paste things. The constants for the dungeon generation are simply the room's minimum and maximum size, and the maximum number of rooms.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">ROOM_MAX_SIZE = <span class="nu0">10</span>
ROOM_MIN_SIZE = <span class="nu0">6</span>
MAX_ROOMS = <span class="nu0">30</span></pre></div></div></div>
<p><br>
Ok, we've described the gist of the algorithm, took care of all helper functions and classes... Now to implement it in <i>make_map</i>. Remove the previous code that creates the example rooms and tunnel, and make a loop to iterate until the maximum number of rooms, assigning random coordinates and size to each one as we go.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">    rooms = <span class="br0">[</span><span class="br0">]</span>
    num_rooms = <span class="nu0">0</span>
&nbsp;
    <span class="kw1">for</span> r <span class="kw1">in</span> <span class="kw2">range</span><span class="br0">(</span>MAX_ROOMS<span class="br0">)</span>:
        <span class="co1">#random width and height</span>
        w = libtcod.<span class="me1">random_get_int</span><span class="br0">(</span><span class="nu0">0</span>, ROOM_MIN_SIZE, ROOM_MAX_SIZE<span class="br0">)</span>
        h = libtcod.<span class="me1">random_get_int</span><span class="br0">(</span><span class="nu0">0</span>, ROOM_MIN_SIZE, ROOM_MAX_SIZE<span class="br0">)</span>
        <span class="co1">#random position without going out of the boundaries of the map</span>
        x = libtcod.<span class="me1">random_get_int</span><span class="br0">(</span><span class="nu0">0</span>, <span class="nu0">0</span>, MAP_WIDTH - w - <span class="nu0">1</span><span class="br0">)</span>
        y = libtcod.<span class="me1">random_get_int</span><span class="br0">(</span><span class="nu0">0</span>, <span class="nu0">0</span>, MAP_HEIGHT - h - <span class="nu0">1</span><span class="br0">)</span></pre></div></div></div>
<p><br>
The <i>random_get_int</i> function returns a random integer between two numbers; the first parameter, zero, identifies the "stream" to get that number from. Random number streams are useful if you want to recreate sequences of random numbers; for our purposes it's not needed, so we use the default stream.
</p><p>The <i>Rect</i> class will really start to come in handy at this point! The <i>rooms</i> list stores all previous rooms as <i>Rect</i> 's. That way we can compare the new room with previous ones for intersection, and reject it if it overlaps.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">        <span class="co1">#"Rect" class makes rectangles easier to work with</span>
        new_room = Rect<span class="br0">(</span>x, y, w, h<span class="br0">)</span>
&nbsp;
        <span class="co1">#run through the other rooms and see if they intersect with this one</span>
        failed = <span class="kw2">False</span>
        <span class="kw1">for</span> other_room <span class="kw1">in</span> rooms:
            <span class="kw1">if</span> new_room.<span class="me1">intersect</span><span class="br0">(</span>other_room<span class="br0">)</span>:
                failed = <span class="kw2">True</span>
                <span class="kw1">break</span></pre></div></div></div>
<p><br>
If the room is valid, we can carve it with <i>create_room</i>! We'll also handle a special case: the player will start in the center of the first room.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">        <span class="kw1">if</span> <span class="kw1">not</span> failed:
            <span class="co1">#this means there are no intersections, so this room is valid</span>
&nbsp;
            <span class="co1">#"paint" it to the map's tiles</span>
            create_room<span class="br0">(</span>new_room<span class="br0">)</span>
&nbsp;
            <span class="co1">#center coordinates of new room, will be useful later</span>
            <span class="br0">(</span>new_x, new_y<span class="br0">)</span> = new_room.<span class="me1">center</span><span class="br0">(</span><span class="br0">)</span>
&nbsp;
            <span class="kw1">if</span> num_rooms == <span class="nu0">0</span>:
                <span class="co1">#this is the first room, where the player starts at</span>
                player.<span class="me1">x</span> = new_x
                player.<span class="me1">y</span> = new_y</pre></div></div></div>
<p><br>
The tunnels deserve some attention. (This is a long example, but please be patient!) Sometimes a strictly horizontal or vertical tunnel can't connect two rooms. Imagine one is on one corner of the map (e.g., top-left), and the other in the opposing corner (e.g., bottom-right.) You'd need two tunnels going from the first room: one horizontal tunnel to reach the right side of the map, and one vertical tunnel to go from there to the bottom of the map (reaching the second room). Or the other way around: one vertical tunnel to reach the bottom of the map, and another horizontal one to reach the right side (reaching the second room.) Both options are valid, so we choose between them randomly.
</p><p>So we're going to code that, and you'll see that this not only covers extreme cases like the one in the example, but also works perfectly when two rooms are side-by-side! Since in that case one of the tunnel segments (horizontal or vertical) will be very small, and only the other segment will be visible. It's a bit hard to visualize though, you'll have to see it in action.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">            <span class="kw1">else</span>:
                <span class="co1">#all rooms after the first:</span>
                <span class="co1">#connect it to the previous room with a tunnel</span>
&nbsp;
                <span class="co1">#center coordinates of previous room</span>
                <span class="br0">(</span>prev_x, prev_y<span class="br0">)</span> = rooms<span class="br0">[</span>num_rooms-<span class="nu0">1</span><span class="br0">]</span>.<span class="me1">center</span><span class="br0">(</span><span class="br0">)</span>
&nbsp;
                <span class="co1">#draw a coin (random number that is either 0 or 1)</span>
                <span class="kw1">if</span> libtcod.<span class="me1">random_get_int</span><span class="br0">(</span><span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">1</span><span class="br0">)</span> == <span class="nu0">1</span>:
                    <span class="co1">#first move horizontally, then vertically</span>
                    create_h_tunnel<span class="br0">(</span>prev_x, new_x, prev_y<span class="br0">)</span>
                    create_v_tunnel<span class="br0">(</span>prev_y, new_y, new_x<span class="br0">)</span>
                <span class="kw1">else</span>:
                    <span class="co1">#first move vertically, then horizontally</span>
                    create_v_tunnel<span class="br0">(</span>prev_y, new_y, prev_x<span class="br0">)</span>
                    create_h_tunnel<span class="br0">(</span>prev_x, new_x, new_y<span class="br0">)</span>
&nbsp;
            <span class="co1">#finally, append the new room to the list</span>
            rooms.<span class="me1">append</span><span class="br0">(</span>new_room<span class="br0">)</span>
            num_rooms += <span class="nu0">1</span></pre></div></div></div>
<p><br>
You made it through all that in one piece, eh? Let's test it out! With some luck, some of the dungeons you get can be pretty amazing. Not too shabby for our own little algorithm!
</p><p>The final code for the dungeon generator is <a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3_code#Dungeon_generator" title="Complete Roguelike Tutorial, using python+libtcod, part 3 code">here</a>.
</p>
<h2> <span class="mw-headline" id="Optional_part:_Visualizing_the_dungeon">Optional part: Visualizing the dungeon</span></h2>
<p>If you're looking at the maps this program is creating and trying to figure out just how the code works, or you've tried playing around with the code to create a dungeon differently but it isn't doing quite what you expected, you can add a label to each room to show the order in which they were created. The game works with single characters, and we will often have more than 10 rooms, so a number doesn't work. We'll put a letter in the middle of each room instead. Add these lines after <code>(new_x, new_y) = new_room.center()</code>.
</p><p><br>
</p>
<div style="padding: 5px; border: solid 1px #C0C0C0; background-color: #F0F0F0"><div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="python source-python"><pre class="de1">            <span class="co1">#optional: print "room number" to see how the map drawing worked</span>
            <span class="co1">#          we may have more than ten rooms, so print 'A' for the first room, 'B' for the next...</span>
            room_no = Object<span class="br0">(</span>new_x, new_y, <span class="kw2">chr</span><span class="br0">(</span><span class="nu0">65</span>+num_rooms<span class="br0">)</span>, libtcod.<span class="me1">white</span><span class="br0">)</span>
            objects.<span class="me1">insert</span><span class="br0">(</span><span class="nu0">0</span>, room_no<span class="br0">)</span> <span class="co1">#draw early, so monsters are drawn on top</span></pre></div></div></div>
<p><br>
This allows for 26 rooms. MAX_ROOMS is set to 30, but it would be extremely unlikely that all 30 would be created without any overlaps. However, if you tweak some of the code and do end up with more than 30 rooms, this will print various symbols such as '[' and then lower case letters. It won't fail unless you have more than 62 rooms.
</p><p>Because this is optional code, it isn't included in the code samples linked from each part of the tutorial.
</p><p><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_4" title="Complete Roguelike Tutorial, using python+libtcod, part 4">Go on to the next part</a>.
</p>
<!-- 
NewPP limit report
Preprocessor node count: 185/1000000
Post-expand include size: 0/2097152 bytes
Template argument size: 0/2097152 bytes
Expensive parser function count: 0/100
-->

<!-- Saved in parser cache with key 133099-basin:pcache:idhash:3607-0!*!0!!en!*!* and timestamp 20131206115242 -->
</div><div class="printfooter">
Retrieved from "<a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3&oldid=30347">http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3&amp;oldid=30347</a>"</div>
		<div id="catlinks" class="catlinks"><div id="mw-normal-catlinks" class="mw-normal-catlinks"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Special:Categories" title="Special:Categories">Category</a>: <ul><li><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Category:Developing" title="Category:Developing">Developing</a></li></ul></div></div>		<!-- end content -->
				<div class="visualClear"></div>
	</div>
</div></div>
<div id="column-one">
	<div id="p-cactions" class="portlet">
		<h5>Views</h5>
		<div class="pBody">
			<ul>
				<li id="ca-nstab-main" class="selected"><a href="./Complete Roguelike Tutorial, using python+libtcod, part 3 - RogueBasin_files/Complete Roguelike Tutorial, using python+libtcod, part 3 - RogueBasin.html" primary="1" context="subject" title="View the content page [alt-c]" accesskey="c">Page</a></li>
				<li id="ca-talk" class="new"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Talk:Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3&action=edit&redlink=1" primary="1" context="talk" title="Discussion about the content page [alt-t]" accesskey="t">Discussion</a></li>
				<li id="ca-viewsource"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3&action=edit" primary="1" title="This page is protected.
You can view its source [alt-e]" accesskey="e">View source</a></li>
				<li id="ca-history"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3&action=history" rel="archives" title="Past revisions of this page [alt-h]" accesskey="h">History</a></li>
			</ul>
		</div>
	</div>
	<div class="portlet" id="p-personal">
		<h5>Personal tools</h5>
		<div class="pBody">
			<ul>
				<li id="pt-login"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Special:UserLogin&returnto=Complete+Roguelike+Tutorial%2C+using+python%2Blibtcod%2C+part+3" title="You are encouraged to log in; however, it is not mandatory [alt-o]" accesskey="o">Log in / create account</a></li>
			</ul>
		</div>
	</div>
	<div class="portlet" id="p-logo">
<a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Main_Page" style="background-image: url(/skins/roguebasin/RogueBasin_logo.png);" title="Visit the main page"></a>
	</div>
	<div class="generated-sidebar portlet" id="p-navigation">
		<h5>Navigation</h5>
		<div class="pBody">
			<ul>
				<li id="n-mainpage"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Main_Page" title="Visit the main page [alt-z]" accesskey="z">Main Page</a></li>
				<li id="n-portal"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=RogueBasin:Community_portal" title="About the project, what you can do, where to find things">Community portal</a></li>
				<li id="n-recentchanges"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Special:RecentChanges" title="A list of recent changes in the wiki [alt-r]" accesskey="r">Recent changes</a></li>
				<li id="n-randompage"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Special:Random" title="Load a random page [alt-x]" accesskey="x">Random page</a></li>
				<li id="n-help"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Help" title="The place to find out">Help</a></li>
			</ul>
		</div>
	</div>
	<div id="p-search" class="portlet">
		<h5><label for="searchInput">Search</label></h5>
		<div id="searchBody" class="pBody">
			<form action="http://roguebasin.roguelikedevelopment.org/index.php" id="searchform">
				<input type="hidden" name="title" value="Special:Search">
				<input type="search" name="search" title="Search RogueBasin [alt-f]" accesskey="f" id="searchInput">
				<input type="submit" name="go" value="Go" title="Go to a page with this exact name if exists" id="searchGoButton" class="searchButton">&nbsp;
				<input type="submit" name="fulltext" value="Search" title="Search the pages for this text" id="mw-searchButton" class="searchButton">
			</form>
		</div>
	</div>
	<div class="portlet" id="p-tb">
		<h5>Toolbox</h5>
		<div class="pBody">
			<ul>
				<li id="t-whatlinkshere"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Special:WhatLinksHere/Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3" title="A list of all wiki pages that link here [alt-j]" accesskey="j">What links here</a></li>
				<li id="t-recentchangeslinked"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Special:RecentChangesLinked/Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3" title="Recent changes in pages linked from this page [alt-k]" accesskey="k">Related changes</a></li>
				<li id="t-specialpages"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Special:SpecialPages" title="A list of all special pages [alt-q]" accesskey="q">Special pages</a></li>
				<li><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3&printable=yes" rel="alternate">Printable version</a></li>
				<li id="t-permalink"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_3&oldid=30347" title="Permanent link to this revision of the page">Permanent link</a></li>
			</ul>
		</div>
	</div>
</div><!-- end of the left (by default at least) column -->
<div class="visualClear"></div>
<div id="footer">
	<div id="f-poweredbyico">
		<a href="http://www.mediawiki.org/"><img src="./Complete Roguelike Tutorial, using python+libtcod, part 3 - RogueBasin_files/poweredby_mediawiki_88x31.png" alt="Powered by MediaWiki" width="88" height="31"></a>
	</div>
	<ul id="f-list">
		<li id="lastmod"> This page was last modified on 26 November 2012, at 02:10.</li>
		<li id="viewcount">This page has been accessed 24,941 times.</li>
		<li id="privacy"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=RogueBasin:Privacy_policy" title="RogueBasin:Privacy policy">Privacy policy</a></li>
		<li id="about"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=RogueBasin:About" title="RogueBasin:About">About RogueBasin</a></li>
		<li id="disclaimer"><a href="http://roguebasin.roguelikedevelopment.org/index.php?title=RogueBasin:General_disclaimer" title="RogueBasin:General disclaimer">Disclaimers</a></li>
	</ul>
</div>
</div>
<script>if(window.mw){
mw.loader.load(["mediawiki.user","mediawiki.page.ready"], null, true);
}</script><script src="./Complete Roguelike Tutorial, using python+libtcod, part 3 - RogueBasin_files/load(3).php" type="text/javascript"></script>
<script src="./Complete Roguelike Tutorial, using python+libtcod, part 3 - RogueBasin_files/load(4).php"></script>
<!-- Served in 0.202 secs. --><autoscroll_cursor hidden=""></autoscroll_cursor></body></html>